﻿using System;
using System.Collections.Generic;
using System.Linq;
using Cms.Application.Admin.Views;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.Comparers;
using Cms.Domain.RepositoryInterfaces;
using Cms.Utilities;

namespace Cms.Application.Admin.Presenters
{
    public class EditPagePresenter : BasePresenter
    {
        private readonly IEditPageView _view;

        public IDiskAccess DiskAccess { get; set; }

        protected Administrator Administrator { get; set; }

        public EditPagePresenter(IEditPageView view)
            : this(view, new RepositoryFactory())
        {
            DiskAccess = new DiskAccess();
        }

        public EditPagePresenter(IEditPageView view, IRepositoryFactory repositoryFactory)
            : base(view, repositoryFactory)
        {
            _view = view;
            Administrator = AdministratorRepository.GetAdministrator(_view.AdministratorID);
            if (!Administrator.HasPermission.EditPage)
                _view.RedirectToPermissionDeniedPage();
        }

        public void UpdateLocales()
        {
            _view.SetLocales(Administrator.CurrentSite.Locales);
        }
        
        public virtual void Initialize()
        {
            _view.SetFolders(Administrator.CurrentSite.Domain, Administrator.CurrentSite.GetRootFolders());
            _view.SetMasterTemplates(Administrator.CurrentSite.MasterTemplates);
            _view.SetDetailTemplates(Administrator.CurrentSite.DetailTemplates);
            _view.SelectedLocaleID = Administrator.CurrentSiteLocale != null
                                         ? Administrator.CurrentSiteLocale.ID
                                         : Administrator.CurrentSite.DefaultLocale.ID;
            SetVersions();

            // This check must be done after SetVersions, since that method also affects the possibility to publish
            if (!Administrator.HasPermission.PublishContent)
            {
                _view.PublishContentNotAllowed();
            }
            if (_view.PageID == -1)
            {
                if (GetType() != typeof(EditApplicationPagePresenter))
                {
                    Folder initialFolder = FolderRepository.GetFolder(Administrator.SelectedFolderID);
                    _view.SelectedFolderID = initialFolder != null ? Administrator.SelectedFolderID : -1;
                }
                else
                    _view.SelectedFolderID = -1;
                if (Administrator.CurrentSite.DefaultMasterTemplate != null)
                    _view.SelectedMasterTemplateID = Administrator.CurrentSite.DefaultMasterTemplate.ID;
                if (Administrator.CurrentSite.DefaultDetailTemplate != null)
                    _view.SelectedDetailTemplateID = Administrator.CurrentSite.DefaultDetailTemplate.ID;
                Folder selectedFolder = FolderRepository.GetFolder(Administrator.SelectedFolderID);
                IList<Category> folderCategories = selectedFolder != null
                                                       ? selectedFolder.Categories
                                                       : new List<Category>();
                _view.SetCategories(Administrator.CurrentSite.GetRootCategories(), folderCategories, new List<Category>());
                IList<AdministratorGroup> folderAllowedAdministratorGroups = selectedFolder != null
                                                                                 ? selectedFolder.GetAllAllowedAdministratorGroups()
                                                                                 : new List<AdministratorGroup>();
                _view.SetAllowedAdministratorGroups(AdministratorRepository.GetCustomGroups(), folderAllowedAdministratorGroups, new List<AdministratorGroup>());
                IList<AdministratorGroup> folderDeniedAdministratorGroups = selectedFolder != null
                                                                                ? selectedFolder.GetAllDeniedAdministratorGroups()
                                                                                : new List<AdministratorGroup>();
                _view.SetDeniedAdministratorGroups(AdministratorRepository.GetCustomGroups(), folderDeniedAdministratorGroups, new List<AdministratorGroup>());
                IList<UserGroup> folderAllowedUserGroups = selectedFolder != null
                                                               ? selectedFolder.GetRequiredUserGroups()
                                                               : new List<UserGroup>();
                _view.SetRequiredUserGroups(UserRepository.GetUserGroups(), folderAllowedUserGroups, new List<UserGroup>(), new List<UserGroup>());
            }
            else
            {
                RefreshProperties();
            }
        }

        public void UpdateHeading()
        {
            _view.HeadingText = _view.PageID > 0
                                    ? GetText(_view, "EditPageHeading") + " &mdash; " + _view.Name
                                    : GetText(_view, "NewPageHeading");
        }

        public void UpdateCategoriesAndGroups()
        {
            Folder selectedFolder = FolderRepository.GetFolder(_view.SelectedFolderID);
            IList<Category> folderCategories = selectedFolder != null
                                                   ? selectedFolder.GetAllCategories()
                                                   : new List<Category>();
            _view.SetCategories(Administrator.CurrentSite.GetRootCategories(), folderCategories, _view.GetSelectedCategories().ToList());

            // Administrator groups
            IList<AdministratorGroup> folderAllowedAdministratorGroups = selectedFolder != null
                                                                             ? selectedFolder.GetAllAllowedAdministratorGroups()
                                                                             : new List<AdministratorGroup>();
            _view.SetAllowedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(), 
                folderAllowedAdministratorGroups, 
                _view.GetSelectedAllowedAdministratorGroups().ToList());
            IList<AdministratorGroup> folderDeniedAdministratorGroups = selectedFolder != null
                                                                            ? selectedFolder.GetAllDeniedAdministratorGroups()
                                                                            : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                folderDeniedAdministratorGroups,
                _view.GetSelectedDeniedAdministratorGroups().ToList());

            // User groups
            IList<UserGroup> folderRequiredUserGroups = selectedFolder != null
                                                            ? selectedFolder.GetRequiredUserGroups()
                                                            : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(),
                folderRequiredUserGroups,
                _view.GetSelectedRequiredUserGroups().ToList(),
                _view.GetDeselectedUserGroups().ToList());
        }
        
        public void RefreshProperties()
        {
            if (_view.PageID < 0)
            {
                return;
            }
            Page page = PageRepository.GetPage(_view.PageID);
            if (!page.AllowsAdministrator(Administrator))
            {
                _view.RedirectToPermissionDeniedPage();
                return;
            }
            _view.Filename = page.Filename.Substring(0, page.Filename.Length - 5); // Remove ".aspx"
            _view.Name = page.Name;
            if (page.Folder == null)
            {
                _view.SelectedFolderID = -1;
            }
            else
            {
                _view.SelectedFolderID = page.Folder.ID;
            }
            _view.SelectedMasterTemplateID = page.MasterTemplate.ID;
            _view.SelectedDetailTemplateID = page.DetailTemplate.ID;
            _view.StartPublishing = page.StartPublishing;
            _view.EndPublishing = page.EndPublishing;
            _view.SortKey = page.SortKey;
            IList<Category> folderCategories = page.Folder != null
                                                   ? page.Folder.GetAllCategories()
                                                   : new List<Category>();
            _view.SetCategories(Administrator.CurrentSite.GetRootCategories(), folderCategories, page.Categories);
            _view.HeadHtml = page.HeadHtml;
            _view.BodyTopHtml = page.BodyTopHtml;
            _view.BodyBottomHtml = page.BodyBottomHtml;
            IList<AdministratorGroup> folderAllowedAdministratorGroups = page.Folder != null
                                                                             ? page.Folder.GetAllAllowedAdministratorGroups()
                                                                             : new List<AdministratorGroup>();
            _view.SetAllowedAdministratorGroups(AdministratorRepository.GetCustomGroups(), folderAllowedAdministratorGroups, page.AllowedAdministratorGroups);
            IList<AdministratorGroup> folderDeniedAdministratorGroups = page.Folder != null
                                                                            ? page.Folder.GetAllDeniedAdministratorGroups()
                                                                            : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(AdministratorRepository.GetCustomGroups(), folderDeniedAdministratorGroups, page.DeniedAdministratorGroups);
            IList<UserGroup> folderAllowedUserGroups = page.Folder != null
                                                           ? page.Folder.GetRequiredUserGroups()
                                                           : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(),
                folderAllowedUserGroups,
                page.RequiredUserGroups,
                page.DeselectedUserGroups);
            if (page.LocalizedProperties.ContainsKey(_view.SelectedLocaleID))
            {
                _view.PageTitle = page.LocalizedProperties[_view.SelectedLocaleID].Title;
                _view.PageDescription = page.LocalizedProperties[_view.SelectedLocaleID].Description;
                _view.PageKeywords = page.LocalizedProperties[_view.SelectedLocaleID].Keywords;
                _view.ListingsTitle = page.LocalizedProperties[_view.SelectedLocaleID].ListingsTitle;
                _view.ListingsDescription = page.LocalizedProperties[_view.SelectedLocaleID].ListingsDescription;
            }
            else
            {
                _view.PageTitle = string.Empty;
                _view.PageDescription = string.Empty;
                _view.PageKeywords = string.Empty;
                _view.ListingsTitle = string.Empty;
                _view.ListingsDescription = string.Empty;
            }
        }

        public void SaveSelectedLocale()
        {
            Administrator.CurrentSiteLocale = LocaleRepository.GetLocale(_view.SelectedLocaleID);
            AdministratorRepository.SaveAdministrator(Administrator);
        }

        public void SetVersions()
        {
            if (_view.PageID > 0)
            {
                Page page = PageRepository.GetPage(_view.PageID);
                IList<ContentTranslationVersion> versions = page.GetContentVersions(_view.SelectedLocaleID);
                int publishedVersionID = -1;
                if (page.ContentSets.ContainsKey(_view.SelectedLocaleID) && page.ContentSets[_view.SelectedLocaleID].PublishedVersion != null)
                {
                    publishedVersionID = page.ContentSets[_view.SelectedLocaleID].PublishedVersion.ID;
                }
                _view.SetVersions(versions, publishedVersionID);
                List<ScheduledPublishing> scheduledPublishings = versions.SelectMany(ctv => ctv.ScheduledPublishings).ToList();
                scheduledPublishings.Sort(new ScheduledPublishingComparer(SortDirection.Ascending));
                _view.SetScheduledPublishings(scheduledPublishings);
                _view.ShowCopyFromDefaultLocale = _view.SelectedLocaleID != Administrator.CurrentSite.DefaultLocale.ID && versions.Count == 0;
            }
            else
            {
                _view.SetVersions(new List<ContentTranslationVersion>(), -1);
                _view.SetScheduledPublishings(new List<ScheduledPublishing>());
            }
        }

        public virtual void Validate()
        {
            if (_view.CurrentTab != "basicproperties")
            {
                return;
            }
            if (_view.Name.Trim() == string.Empty)
            {
                AddValidationError(1, GetText(_view, "NoNameError"));
            }
            if (_view.Filename.Trim() == string.Empty)
            {
                AddValidationError(2, GetText(_view, "NoFilenameError"));
            }
            else if (!RegexValidation.NameIsFileSystemCompliant(_view.Filename.Trim()))
            {
                AddValidationError(4, GetText(_view, "NameNotFileSystemCompliantError"));
            }
            string filename = _view.Filename;
            if (!filename.ToLower().EndsWith(".aspx"))
            {
                filename += ".aspx";
            }
            if (!Administrator.CurrentSite.CanPageBeNamedAndMovedTo(_view.PageID, filename, FolderRepository.GetFolder(_view.SelectedFolderID)))
            {
                AddValidationError(3, GetText(_view, "FilenameCollisionError"));
            }
        }
        
        public void GetBasicPropertiesViewData(Page page)
        {
            page.Filename = _view.Filename;
            if (!page.Filename.ToLower().EndsWith(".aspx"))
            {
                page.Filename += ".aspx";
            }
            page.Name = _view.Name;
            page.Folder = FolderRepository.GetFolder(_view.SelectedFolderID);
            page.MasterTemplate = SiteRepository.GetMasterTemplate(_view.SelectedMasterTemplateID);
            page.DetailTemplate = SiteRepository.GetDetailTemplate(_view.SelectedDetailTemplateID);
            page.StartPublishing = _view.StartPublishing;
            page.EndPublishing = _view.EndPublishing;
            page.SortKey = _view.SortKey;
            page.HeadHtml = _view.HeadHtml;
            page.BodyTopHtml = _view.BodyTopHtml;
            page.BodyBottomHtml = _view.BodyBottomHtml;
            foreach (Category category in page.Categories)
            {
                category.Pages.Remove(page);
            }
            page.Categories.Clear();
            foreach (Category category in _view.GetSelectedCategories())
            {
                page.Categories.Add(category);
                category.Pages.Add(page);
            }
            foreach (AdministratorGroup group in page.AllowedAdministratorGroups)
            {
                group.AllowedPages.Remove(page);
            }
            page.AllowedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedAllowedAdministratorGroups())
            {
                page.AllowedAdministratorGroups.Add(group);
                group.AllowedPages.Add(page);
            }
            foreach (AdministratorGroup group in page.DeniedAdministratorGroups)
            {
                group.DeniedPages.Remove(page);
            }
            page.DeniedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedDeniedAdministratorGroups())
            {
                page.DeniedAdministratorGroups.Add(group);
                group.DeniedPages.Add(page);
            }
            foreach (UserGroup group in page.RequiredUserGroups)
            {
                group.SelectedPages.Remove(page);
            }
            page.RequiredUserGroups.Clear();
            foreach (UserGroup group in _view.GetSelectedRequiredUserGroups())
            {
                page.RequiredUserGroups.Add(group);
                group.SelectedPages.Add(page);
            }
            foreach (UserGroup group in page.DeselectedUserGroups)
                group.DeselectedPages.Remove(page);
            page.DeselectedUserGroups.Clear();
            foreach (UserGroup group in _view.GetDeselectedUserGroups())
            {
                page.DeselectedUserGroups.Add(group);
                group.DeselectedPages.Add(page);
            }
        }
        
        public void GetLocalizedPropertiesViewData(Page page)
        {
            if (!page.LocalizedProperties.ContainsKey(_view.SelectedLocaleID))
            {
                page.LocalizedProperties.Add(_view.SelectedLocaleID, new PageLocalizedProperties());
            }
            page.LocalizedProperties[_view.SelectedLocaleID].Title = _view.PageTitle;
            page.LocalizedProperties[_view.SelectedLocaleID].Description = _view.PageDescription;
            page.LocalizedProperties[_view.SelectedLocaleID].Keywords = _view.PageKeywords;
            page.LocalizedProperties[_view.SelectedLocaleID].ListingsTitle = _view.ListingsTitle;
            page.LocalizedProperties[_view.SelectedLocaleID].ListingsDescription = _view.ListingsDescription;
        }
        
        public virtual void Save()
        {
            Validate();
            if (!IsValid)
            {
                return;
            }
            Page page = _view.PageID == -1
                            ? new Page { Site = Administrator.CurrentSite }
                            : PageRepository.GetPage(_view.PageID);
            string originalVirtualPath = page.GetVirtualPath();
            GetBasicPropertiesViewData(page);
            GetLocalizedPropertiesViewData(page);

            //TODO: Check Areas!!!
            PageRepository.SavePage(page);
            if (_view.PageID > 0 && page.GetVirtualPath() != originalVirtualPath)
            {
                var frm = new FileReferencesManager(Administrator, SiteRepository, ContentRepository, DiskAccess);
                frm.UpdateFileReferences(originalVirtualPath, page.GetVirtualPath(), Administrator.CurrentSite.ID);
            }
            if (GetType() != typeof(EditApplicationPagePresenter))
            {
                Administrator.SelectedFolderID = page.Folder != null ? page.Folder.ID : -1;
                AdministratorRepository.SaveAdministrator(Administrator);
            }
            _view.SetPageID(page.ID);
        }
        
        public void Publish()
        {
            ContentTranslationVersion ctv = ContentRepository.GetContentTranslationVersion(_view.SelectedVersionID);
            if (ctv == null)
            {
                return;
            }
            ctv.Publish();
            SiteRepository.SaveSite(Administrator.CurrentSite);
            SetVersions();
            _view.SelectedVersionID = ctv.ID;
        }

        public string GetPagePreviewUrl()
        {
            Page page = PageRepository.GetPage(_view.PageID);
            if (page == null)
            {
                return string.Empty;
            }
            Locale locale = LocaleRepository.GetLocale(_view.SelectedLocaleID);
            string localeCode = string.Empty;
            if (locale != null && locale != Administrator.CurrentSite.DefaultLocale)
            {
                localeCode = locale.UrlCode + "/";
            }
            string url = "http://" + page.Site.Domain + "/" + localeCode + page.GetVirtualPath();
            url += "?PreviewMode=Page&VersionID=" + _view.SelectedVersionID;
            return url;
        }

        public IEnumerable<ContentSetContainer> GetContentContainers(string areaID, string contentType)
        {
            ContentSetContainer container = null;
            IList<ContentSetContainer> containers = new List<ContentSetContainer>();
            switch (contentType)
            {
                case "PageContent":
                case "ApplicationPageContent":
                    container = PageRepository.GetPage(_view.PageID);
                    break;
                case "MasterTemplateContent":
                    container = PageRepository.GetPage(_view.PageID).MasterTemplate;
                    break;
                case "DetailTemplateContent":
                    container = PageRepository.GetPage(_view.PageID).DetailTemplate;
                    break;
            }
            if (container == null)
            {
                return containers;
            }
            Content c = container.GetContent(areaID);
            foreach (PageContentLink pl in c.Pages)
            {
                if (pl.Page.ID != _view.PageID)
                {
                    containers.Add(pl.Page);
                }
            }
            foreach (MasterTemplateContentLink mtl in c.MasterTemplates)
            {
                containers.Add(mtl.MasterTemplate);
            }
            foreach (DetailTemplateContentLink dtl in c.DetailTemplates)
            {
                containers.Add(dtl.DetailTemplate);
            }
            return containers;
        }
        
        public void BreakContentLink(string areaID, string contentType)
        {
            ContentSetContainer container = null;
            switch (contentType)
            {
                case "PageContent":
                case "ApplicationPageContent":
                    container = PageRepository.GetPage(_view.PageID);
                    break;
                case "MasterTemplateContent":
                    container = PageRepository.GetPage(_view.PageID).MasterTemplate;
                    break;
                case "DetailTemplateContent":
                    container = PageRepository.GetPage(_view.PageID).DetailTemplate;
                    break;
            }
            if (container == null)
            {
                return;
            }
            Content content = container.GetContent(areaID);
            switch (contentType)
            {
                case "PageContent":
                case "ApplicationPageContent":
                    content.RemovePage((Page)container);
                    break;
                case "MasterTemplateContent":
                    content.RemoveMasterTemplate((MasterTemplate)container);
                    break;
                case "DetailTemplateContent":
                    content.RemoveDetailTemplate((DetailTemplate)container);
                    break;
            }

            foreach (var translation in content.Translations)
            {
                ContentSet set = container.GetContentSet(LocaleRepository.GetLocale(translation.Key));
                foreach (ContentTranslationVersion ctv in translation.Value.Versions)
                {
                    set.TranslationVersions.Remove(ctv);
                }
            }

            //PageRepository.SavePage((Page)container);
            SiteRepository.SaveSite(container.Site);
            SetVersions();
        }

        public void SchedulePublishing(int contentTranslationVersionID, DateTime scheduledPublishingDateTime)
        {
            ContentTranslationVersion ctv = ContentRepository.GetContentTranslationVersion(contentTranslationVersionID);
            if (ctv == null)
            {
                return;
            }
            var scheduledPublishing = new ScheduledPublishing
                                          {
                                              Timestamp = scheduledPublishingDateTime.Ticks.ToString(),
                                              ContentTranslationVersion = ctv,
                                              Description = null
                                          };
            ctv.ScheduledPublishings.Add(scheduledPublishing);
            SiteRepository.SaveSite(Administrator.CurrentSite);
            SetVersions();
        }
        
        public void DeleteScheduledPublishing(int id)
        {
            ContentRepository.DeleteScheduledPublishing(id);
            SetVersions();
        }
    }
}